import struct
import socket
import numpy as np
from cv2 import cv2
import tkinter
import tkinter.messagebox
import threading
from PIL import Image, ImageTk

# 创建视窗
root = tkinter.Tk()

# 放缩大小
scale = 1
# 原传输画面尺寸
fixw, fixh = 0, 0
# 放缩标志
wscale = False
# 屏幕显示画布
showcan = None
bufsize = 1024
soc = None
img = None

# 设置放缩 (滑条)
def SetScale(x):
    global scale, wscale
    scale = float(x) / 100
    wscale = True

# 显示视窗
def ShowScreen():
    global showcan, root, soc, th, wscale
    # 如果视窗画布为空
    if showcan is None:
        wscale = True
        # Toplevel 容器控件， 用于提供一个单独的对话框，和 Frame比较类似
        # 父组件: root
        showcan = tkinter.Toplevel(root)
        # 创建线程
        th = threading.Thread(target=run)
        # 启动线程
        th.start()
    else:
        # 关闭 socket 连接
        soc.close()
        # 销毁该视窗
        showcan.destroy()

# 配置视窗
# StringVar() 用于管理部件上的字符，保证值的变更随时可以显示在界面上
val = tkinter.StringVar()
# 创建部件
host_lab = tkinter.Label(root, text="Host:")
host_en = tkinter.Entry(root, show=None, font=('Arial', 14), textvariable=val)
sca_lab = tkinter.Label(root, text="Scale:")
# 创建滑块
sca = tkinter.Scale(root, from_=10, to=100, orient=tkinter.HORIZONTAL, length=100,
                    showvalue=100, resolution=0.1, tickinterval=50, command=SetScale)
show_btn = tkinter.Button(root, text="Show", command=ShowScreen)

# 设置部件大小
host_lab.grid(row=0, column=0, padx=10, pady=10, ipadx=0, ipady=0)
host_en.grid(row=0, column=1, padx=0, pady=0, ipadx=40, ipady=0)
sca_lab.grid(row=1, column=0, padx=10, pady=10, ipadx=0, ipady=0)
# 滚动条
sca.grid(row=1, column=1, padx=0, pady=0, ipadx=100, ipady=0)
show_btn.grid(row=2, column=1, padx=0, pady=10, ipadx=30, ipady=0)

# 设置初始值
sca.set(100)
val.set('127.0.0.1:80')


def run():
    global soc, img, wscale, showcan, scale, fixh, fixw
    # 获取输入框的值
    host = host_en.get()
    # 判断是否符合条件
    if host is None:
        tkinter.messagebox.showinfo('提示','Host设置错误！！！')
        return
    # 分割字符串
    hs = host.split(":")
    if len(hs) != 2:
        tkinter.messagebox.showinfo('提示','Host设置错误！！！')
        return
    # 设置 socket 连接
    soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 连接 socket
    soc.connect((hs[0], int(hs[1])))
    lenb = soc.recv(4)
    le = struct.unpack(">I", lenb)[0]
    imb = b''
    while le > bufsize:
        t = soc.recv(bufsize)
        imb += t
        le -= len(t)
    while le > 0:
        t = soc.recv(le)
        imb += t
        le -= len(t)
    data = np.frombuffer(imb, dtype=np.uint8)
    img = cv2.imdecode(data, cv2.IMREAD_COLOR)
    # 设置图像大小
    h, w, _ = img.shape
    fixh, fixw = h, w
    imsh = cv2.cvtColor(img, cv2.COLOR_BGR2RGBA)
    # 将数据转换为数组
    imi = Image.fromarray(imsh)
    imgTK = ImageTk.PhotoImage(image=imi)
    # 创建画布
    cv = tkinter.Canvas(showcan, width=w, height=h, bg="white")
    # focus_set() 当且仅当主视窗被聚焦后，这个方法用于将焦点设置在所需要的小部件上
    cv.focus_set()
    # 采用流式布局
    cv.pack()
    # 生成图像
    cv.create_image(0, 0, anchor=tkinter.NW, image=imgTK)
    h = int(h * scale)
    w = int(w * scale)
    while True:
        if wscale:
            h = int(fixh * scale)
            w = int(fixw * scale)
            cv.config(width=w, height=h)
            # 恢复初始值
            wscale = False
        lenb = soc.recv(4)
        le = struct.unpack(">I", lenb)[0]
        imb = b''
        while le > bufsize:
            t = soc.recv(bufsize)
            imb += t
            le -= len(t)
        while le > 0:
            t = soc.recv(le)
            imb += t
            le -= len(t)
        data = np.frombuffer(imb, dtype=np.uint8)
        ims = cv2.imdecode(data, cv2.IMREAD_COLOR)
        img = img + ims
        imt = cv2.resize(img, (w, h))
        imsh = cv2.cvtColor(imt, cv2.COLOR_BGR2RGBA)
        imi = Image.fromarray(imsh)
        # 将新的数据粘贴
        imgTK.paste(imi)


root.mainloop()
